---
title: "Bind to JS Function"
description: "JS interop with functions in ReScript"
canonical: "/docs/manual/bind-to-js-function"
section: "JavaScript Interop"
order: 6
---

# Function

Binding a JS function is like binding any other value:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
// Import nodejs' path.dirname
@module("path") external dirname: string => string = "dirname"
let root = dirname("/User/github") // returns "User"
```

```js
var Path = require("path");
var root = Path.dirname("/User/github");
```

</CodeTab>

We also expose a few special features, described below.

## Labeled Arguments

ReScript has [function](./function.mdx) signature. These work on an `external` too! You'd use them to _fix_ a JS function's unclear usage. Assuming we're modeling this:

```js
// MyGame.js

function draw(x, y, border) {
  // suppose `border` is optional and defaults to false
}
draw(10, 20);
draw(20, 20, true);
```

It'd be nice if on ReScript's side, we can bind & call `draw` while labeling things a bit:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
@module("MyGame")
external draw: (~x: int, ~y: int, ~border: bool=?) => unit = "draw"

draw(~x=10, ~y=20, ~border=true)
draw(~x=10, ~y=20)
```

```js
var MyGame = require("MyGame");

MyGame.draw(10, 20, true);
MyGame.draw(10, 20, undefined);
```

</CodeTab>

We've compiled to the same function, but now the usage is much clearer on the ReScript side thanks to labels!

Note that you can freely reorder the labels on the ReScript side; they'll always correctly appear in their declaration order in the JavaScript output:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
@module("MyGame")
external draw: (~x: int, ~y: int, ~border: bool=?) => unit = "draw"

draw(~x=10, ~y=20)
draw(~y=20, ~x=10)
```

```js
var MyGame = require("MyGame");

MyGame.draw(10, 20, undefined);
MyGame.draw(10, 20, undefined);
```

</CodeTab>

## Object Method

Functions attached to JS objects (other than JS modules) require a special way of binding to them, using `send`:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
type document // abstract type for a document object
@send external getElementById: (document, string) => Dom.element = "getElementById"
@val external doc: document = "document"

let el = getElementById(doc, "myId")
```

```js
var el = document.getElementById("myId");
```

</CodeTab>

In a `send`, the object is always the first argument. Actual arguments of the method follow (this is a bit what modern OOP objects are really).

### Chaining

Ever used `foo().bar().baz()` chaining ("fluent api") in JS OOP? We can model that in ReScript too, through the [pipe operator](./pipe.mdx).

### Nested function call

`@send` can also accept a `@scope(("itemOne","itemTwo"))` to access a function on a nested property.

<CodeTab labels={["ReScript", "JS Output"]}>
```res example
type stripe

@module("stripe") @new
external make: string => stripe = "default"

type createSession = {}

type sessionResult

@send
@scope(("checkout", "sessions"))
external createCheckoutSession: (stripe, createSession) =>
Promise.t<sessionResult> = "create"

let stripe = make("sk\_...")
let session = stripe->createCheckoutSession({})

````
```js
import Stripe from "stripe";

var stripe = new Stripe("sk_...");
var session = stripe.checkout.sessions.create({});
````

</CodeTab>

## Variadic Function Arguments

You might have JS functions that take an arbitrary amount of arguments. ReScript supports modeling those, under the condition that the arbitrary arguments part is homogenous (aka of the same type). If so, add `variadic` to your `external`.

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
@module("path") @variadic
external join: array<string> => string = "join"

let v = join(["a", "b"])
```

```js
var Path = require("path");
var v = Path.join("a", "b");
```

</CodeTab>

`module` will be explained in [Import from/Export to JS](./import-from-export-to-js.mdx).

## Modeling Polymorphic Function

Apart from the above special-case, JS functions in general are often arbitrarily overloaded in terms of argument types and number. How would you bind to those?

### Trick 1: Multiple `external`s

If you can exhaustively enumerate the many forms an overloaded JS function can take, simply bind to each differently:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
@module("MyGame") external drawCat: unit => unit = "draw"
@module("MyGame") external drawDog: (~giveName: string) => unit = "draw"
@module("MyGame") external draw: (string, ~useRandomAnimal: bool) => unit = "draw"
```

```js
// Empty output
```

</CodeTab>

Note how all three externals bind to the same JS function, `draw`.

### Trick 2: Polymorphic Variant + `unwrap`

If you have the irresistible urge of saying "if only this JS function argument was a variant instead of informally being either `string` or `int`", then good news: we do provide such `external` features through annotating a parameter as a polymorphic variant! Assuming you have the following JS function you'd like to bind to:

```js
function padLeft(value, padding) {
  if (typeof padding === "number") {
    return Array(padding + 1).join(" ") + value;
  }
  if (typeof padding === "string") {
    return padding + value;
  }
  throw new Error(`Expected string or number, got '${padding}'.`);
}
```

Here, `padding` is really conceptually a variant. Let's model it as such.

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
@val
external padLeft: (
  string,
  @unwrap [
    | #Str(string)
    | #Int(int)
  ])
  => string = "padLeft"
padLeft("Hello World", #Int(4))
padLeft("Hello World", #Str("Message from ReScript: "))
```

```js
padLeft("Hello World", 4);
padLeft("Hello World", "Message from ReScript: ");
```

</CodeTab>

Obviously, the JS side couldn't have an argument that's a polymorphic variant! But here, we're just piggy backing on poly variants' type checking and syntax. The secret is the `@unwrap` annotation on the type. It strips the variant constructors and compile to just the payload's value. See the output.

## Constrain Arguments Better

Consider the Node `fs.readFileSync`'s second argument. It can take a string, but really only a defined set: `"ascii"`, `"utf8"`, etc. You can still bind it as a string, but we can use poly variants + `string` to ensure that our usage's more correct:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
@module("fs")
external readFileSync: (
  ~name: string,
  @string [
    | #utf8
    | @as("ascii") #useAscii
  ],
) => string = "readFileSync"

readFileSync(~name="xx.txt", #useAscii)
```

```js
var Fs = require("fs");
Fs.readFileSync("xx.txt", "ascii");
```

</CodeTab>

- Attaching `@string` to the whole poly variant type makes its constructor compile to a string of the same name.
- Attaching a `@as("bla")` to a constructor lets you customize the final string.

And now, passing something like `"myOwnUnicode"` or other variant constructor names to `readFileSync` would correctly error.

Aside from string, you can also compile an argument to an int, using `int` instead of `string` in a similar way:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
@val
external testIntType: (
  @int [
    | #onClosed
    | @as(20) #onOpen
    | #inBinary
  ])
  => int = "testIntType"
testIntType(#inBinary)
```

```js
testIntType(21);
```

</CodeTab>

`onClosed` compiles to `0`, `onOpen` to `20` and `inBinary` to **`21`**.

## Unknown for type safety

It is best practice to inspect data received from untrusted external functions to ensure it contains what you expect. This helps avoid run-time crashes and unexpected behavior. If you're certain about what an external function returns, simply assert the return value as `string` or `array<int>` or whatever you want it to be. Otherwise use `unknown`. The ReScript type system will prevent you from using an `unknown` until you first inspect it and "convert" it using JSON parsing utilities or similar tools.

Consider the example below of two external functions that access the value of a property on a JavaScript object. `getPropertyUnsafe` returns an `'a`, which means "anything you want it to be." ReScript allows you to use this value as a `string` or `array` or any other type. Quite convenient! But if the property is missing or contains something unexpected, your code might break. You can make the binding more safe by changing `'a` to `string` or `option<'a>`, but this doesn't completely eliminate the problem.

The `getPropertySafe` function returns an `unknown`, which could be `null` or a `string` or anything else. But ReScript prevents you from using this value inappropriately until it has been safely parsed.

```res example
@get_index external getPropertyUnsafe: ({..}, string) => 'a = ""
@get_index external getPropertySafe: ({..}, string) => unknown = ""

let person = {"name": "Bob", "age": 12}

let greeting1 = "Hello, " ++ getPropertyUnsafe(person, "name") // works (this time!)
// let greeting2 = "Hello, " ++ getPropertySafe(person, "name") // syntax error
```

## Special-case: Event Listeners

One last trick with polymorphic variants:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
type readline

@send
external on: (
    readline,
    @string [
      | #close(unit => unit)
      | #line(string => unit)
    ]
  )
  => readline = "on"

let register = rl =>
  rl
  ->on(#close(event => ()))
  ->on(#line(line => Console.log(line)));
```

```js
function register(rl) {
  return rl
    .on("close", function ($$event) {})
    .on("line", function (line) {
      console.log(line);
    });
}
```

</CodeTab>

{/* TODO: GADT phantom type */}

## Fixed Arguments

Sometimes it's convenient to bind to a function using an `external`, while passing predetermined argument values to the JS function:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
@val
external processOnExit: (
  @as("exit") _,
  int => unit
) => unit = "process.on"

processOnExit(exitCode =>
  Console.log("error code: " ++ Int.toString(exitCode))
);
```

```js
process.on("exit", function (exitCode) {
  console.log("error code: " + exitCode.toString());
});
```

</CodeTab>

The `@as("exit")` and the placeholder `_` argument together indicates that you want the first argument to compile to the string `"exit"`. You can also use any JSON literal with `as`: ``@as(json`true`)``, ``@as(json`{"name": "John"}`)``, etc.

## Ignore arguments

You can also explicitly "hide" `external` function parameters in the JS output, which may be useful if you want to add type constraints to other parameters without impacting the JS side:

<CodeTab labels={["ReScript", "JS Output"]}>

```res
@val external doSomething: (@ignore 'a, 'a) => unit = "doSomething"

doSomething("this only shows up in ReScript code", "test")
```

```js
doSomething("test");
```

</CodeTab>

**Note:** It's a pretty niche feature, mostly used to map to polymorphic JS APIs.

## Modeling `this`-based Callbacks

Many JS libraries have callbacks which rely on this (the source), for example:

```js
x.onload = function (v) {
  console.log(this.response + v);
};
```

Here, `this` would point to `x` (actually, it depends on how `onload` is called, but we digress). It's not correct to declare `x.onload` of type `(. unit) -> unit`. Instead, we introduced a special attribute, `this`, which allows us to type `x` as so:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
type x
@val external x: x = "x"
@set external setOnload: (x, @this ((x, int) => unit)) => unit = "onload"
@get external resp: x => int = "response"
setOnload(x, @this (o, v) => Console.log(resp(o) + v))
```

```js
x.onload = function (v) {
  var o = this;
  console.log((o.response + v) | 0);
};
```

</CodeTab>

`@this` reserves the first parameter for the `this` value, and for arity of 0, there is no need for a redundant `unit` type.

## Function Nullable Return Value Wrapping

For JS functions that return a value that can also be `undefined` or `null`, we provide `@return(...)`. To automatically convert that value to an `option` type (recall that ReScript `option` type's `None` value only compiles to `undefined` and not `null`).

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
type element
type dom

@send @return(nullable)
external getElementById: (dom, string) => option<element> = "getElementById"

let test = dom => {
  let elem = dom->(getElementById("haha"))
  switch (elem) {
  | None => 1
  | Some(_ui) => 2
  }
}
```

```js
function test(dom) {
  var elem = dom.getElementById("haha");
  if (elem == null) {
    return 1;
  } else {
    console.log(elem);
    return 2;
  }
}
```

</CodeTab>

`return(nullable)` attribute will automatically convert `null` and `undefined` to `option` type.

Currently 4 directives are supported: `null_to_opt`, `undefined_to_opt`, `nullable` and `identity`.

{/* When the return type is unit: the compiler will append its return value with an OCaml unit literal to make sure it does return unit. Its main purpose is to make the user consume FFI in idiomatic OCaml code, the cost is very very small and the compiler will do smart optimizations to remove it when the returned value is not used (mostly likely). */}

`identity` will make sure that compiler will do nothing about the returned value. It is rarely used, but introduced here for debugging purpose.

## Tagged template functions

**Since 11.1**

**Experimental** You can easily bind to [JS tagged template functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates).
Tag functions in JS expect as input an array of strings and variadic parameters for the arguments of the interpolation.
To bind to those functions in ReScript, the binding signature must have two arrays as arguments,
the first one being an array of strings and the second can be an array of anything.
You add the `@taggedTemplate` annotation and you're good to go!

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
// see https://bun.sh/docs/runtime/shell
type result = {exitCode: int}
@module("bun") @taggedTemplate
external sh: (array<string>, array<string>) => promise<result> = "$"

let filename = "index.res"
let result = await sh`ls ${filename}`
```

```js
import * as $$Bun from "bun";
var filename = "index.res";
var result = await $$Bun.$`ls ${filename}`;
```

</CodeTab>

Notice that it gets compiled to tagged template literals in JS, which allows
to use JS tools that only work on the literals and not by calling directly the tag function.

There are plenty of useful JS tools you can bind to, like [`gql`](https://github.com/apollographql/graphql-tag),
[`sql`](https://github.com/porsager/postgres), [`css`](https://github.com/mayank99/ecsstatic) and a lot others!
